package lv.senan.clients.bing;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * MS Bing search client. Most of code derived from here:
 * http://www.bing.com/community/blogs/developer/archive/2009/05/28/using-the-api-version-2-0-beta-with-java-and-the-api-s-xml-interface.aspx
 * 
 * @author Igors Gulbinskis
 */
public class BingClient {
	
	private final String APP_ID = "B475FF6C5F412F2AB0E77629278FBD51871DEBBD";
	private final String REQUEST_URL = "http://api.search.live.net/xml.aspx";
	
	public String buildRequest(String query) {
		String request = REQUEST_URL + "?"
			+ "AppId=" + APP_ID
			+ "&Query=" + query
			+ "&Sources=Web"
			/* common request fields (optional) */
			+ "&Version=2.0"
			+ "&Market=lv-LV";
		
		return request;
	}
	
	/**
	 * Builds an xml from raw request.
	 */
	public Document buildDocument(String request) throws SAXException, IOException, ParserConfigurationException {
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
		builderFactory.setNamespaceAware(true);
		DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();

		Document document = null;
		if (documentBuilder != null) {              
			document = documentBuilder.parse(request);
		}
		return document;
	}
	
	/**
	 * Displays response. Method used only for debugging purpose.
	 */
	public void displayResponse(Document document) throws XPathExpressionException {
		XPathFactory factory = XPathFactory.newInstance();
		XPath xpath = factory.newXPath();
		xpath.setNamespaceContext(new BingNamespaceContext());
		
		NodeList errors = (NodeList) xpath.evaluate("//api:Error", document, XPathConstants.NODESET);
		if(errors != null && errors.getLength() > 0) {
			displayErrors(errors, xpath);
		} else{
			displayResults(document, xpath);
		}
	}
	
	private void displayResults(Document document, XPath xpath) throws XPathExpressionException {
		NodeList results = (NodeList) xpath.evaluate("//web:Web/web:Results/web:WebResult", document, XPathConstants.NODESET); 
		
		StringBuilder builder = new StringBuilder();
		for(int i = 0 ; i < results.getLength(); i++) {
			NodeList childNodes = results.item(i).getChildNodes();
			for (int j = 0; j < childNodes.getLength(); j++) {
				if(!childNodes.item(j).getLocalName().equalsIgnoreCase("DisplayUrl")) {
					String fieldName = childNodes.item(j).getLocalName();
					if(fieldName.equalsIgnoreCase("DateTime")) {
						fieldName = "Last Crawled";
					}
					builder.append(fieldName + ": " + childNodes.item(j).getTextContent() + "\n");
				}
			}
			builder.append("\n");
		}
		System.out.println(builder);
	}

	private static void displayErrors(NodeList errors, XPath xpath) {
		/* TODO */
	}	
	
	/**
	 * Returns count of search results.
	 */
	public long getResultsCount(Document document) throws XPathExpressionException {
		XPathFactory factory = XPathFactory.newInstance();
		XPath xpath = factory.newXPath();
		xpath.setNamespaceContext(new BingNamespaceContext());
		
		NodeList errors = (NodeList) xpath.evaluate("//api:Error", document, XPathConstants.NODESET);
		if(errors != null && errors.getLength() > 0) {
			return -1;
		} else{
			return Long.parseLong((String) xpath.evaluate("//web:Web/web:Total", document, XPathConstants.STRING));
		}		
	}
	
}
